package com.atguigu.gulimall.order.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.atguigu.common.constant.OrderConstant;
import com.atguigu.common.utils.R;
import com.atguigu.common.vo.MemberRespVo;
import com.atguigu.gulimall.order.dao.OrderItemDao;
import com.atguigu.gulimall.order.entity.OrderItemEntity;
import com.atguigu.gulimall.order.entity.OrderReturnReasonEntity;
import com.atguigu.gulimall.order.feign.CartFeignService;
import com.atguigu.gulimall.order.feign.MemberFeignService;
import com.atguigu.gulimall.order.feign.ProductFeignService;
import com.atguigu.gulimall.order.feign.WareFeignService;
import com.atguigu.gulimall.order.interceptor.LoginInterceptor;
import com.atguigu.gulimall.order.service.OrderItemService;
import com.atguigu.gulimall.order.to.OrderCreateTo;
import com.atguigu.gulimall.order.vo.*;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.rabbitmq.client.Channel;
import net.bytebuddy.asm.Advice;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;

import com.atguigu.gulimall.order.dao.OrderDao;
import com.atguigu.gulimall.order.entity.OrderEntity;
import com.atguigu.gulimall.order.service.OrderService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

@RabbitListener(queues = {"hello-queue"})
@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {

    @Autowired
    MemberFeignService memberFeignService;

    @Autowired
    CartFeignService cartFeignService;

    @Autowired
    ThreadPoolExecutor executor;

    @Autowired
    WareFeignService wareFeignService;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    ProductFeignService productFeignService;

    @Autowired
    OrderService orderService;

    OrderItemService orderItemService;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }

    /**
     * 获取订单确认页面的数据
     * @return
     */
    @Override
    public OrderConfirmVo confirmOrder() {
        OrderConfirmVo confirmVo = new OrderConfirmVo();
        MemberRespVo member = LoginInterceptor.loginUser.get();
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        CompletableFuture<Void> addressFuture = CompletableFuture.runAsync(() -> {
//      远程查询用户的收货地址

            /**
             * 异步编排共享请求处理线程的request上下文，这样feign的拦截器里面才能获取到上下文
             * 并携带请求头远程调用
             */
            RequestContextHolder.setRequestAttributes(requestAttributes);
            confirmVo.setAddressList(memberFeignService.getAddress(member.getId()));
        }, executor);

        CompletableFuture<Void> cartFuture = CompletableFuture.runAsync(() -> {
//        远程查询购物车服务
            RequestContextHolder.setRequestAttributes(requestAttributes);

            List<OrderItemVo> currentUserCartItems = cartFeignService.getCurrentUserCartItems();
            confirmVo.setOrderItemList(currentUserCartItems);
            BigDecimal bigDecimal = currentUserCartItems.stream().map(item -> {
                return item.getPrice().multiply(new BigDecimal(item.getCount()));
            }).reduce(BigDecimal::add).get();
            confirmVo.setTotal(bigDecimal);

        }, executor).thenRunAsync(()->{
//            查询出购物车中的商品信息还需要查询商品的库存信息
            List<OrderItemVo> orderItemList = confirmVo.getOrderItemList();
            List<Long> collect = orderItemList.stream().map(OrderItemVo::getSkuId).collect(Collectors.toList());
            R skuHasStock = wareFeignService.getSkuHasStock(collect);
            List<SkuStockVo> data = skuHasStock.getData(new TypeReference<List<SkuStockVo>>() {});

            if(data!=null){
                Map<Long, Boolean> map = data.stream().collect(Collectors.toMap(SkuStockVo::getSkuId, SkuStockVo::getHasStock));
                confirmVo.setHasStock(map);
            }
        },executor);

        CompletableFuture.allOf(addressFuture,cartFuture);

//        优惠券信息(积分)
        confirmVo.setIntegration(member.getIntegration());

//      TODO 防止重复提交令牌
        String uuid = UUID.randomUUID().toString().replace("-", "");
        confirmVo.setToken(uuid);
        redisTemplate.opsForValue().set(OrderConstant.ORDER_TOKEN_PREFIX+member.getId(),uuid,30, TimeUnit.MINUTES);

        return confirmVo;
    }

    @Transactional
    @Override
    public SubmitOrderResponseVo submitOrder(OrderSubmitVo vo) {
        SubmitOrderResponseVo response = new SubmitOrderResponseVo();
        MemberRespVo member = LoginInterceptor.loginUser.get();
//        验证token
        String orderToken = vo.getOrderToken();

//        原子验证令牌和删除
        String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
        Long result = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(OrderConstant.ORDER_TOKEN_PREFIX + member.getId()), orderToken);
//        String memberToken = redisTemplate.opsForValue().get(OrderConstant.ORDER_TOKEN_PREFIX + member.getId());
        if(orderToken == null || result == null || result == 0){
            response.setCode(1);
            return response;
        }
//            令牌验证通过
//        创建订单
        OrderCreateTo order = createOrder(vo,member.getId());
//        检查价格
        if(Math.abs(order.getOrder().getPayAmount().subtract(vo.getPayPrice()).doubleValue()) >= 0.01){
            response.setCode(1);
            return response;
        }
//            金额对比通过

//        保存订单
        saveOrder(order);
//        锁定库存  订单号，所有订单项(skuId,skuNum,skuName)
        WareSkuLockVo lockVo = new WareSkuLockVo();
        lockVo.setOrderSn(order.getOrder().getOrderSn());
        List<OrderItemVo> locks = order.getOrderItem().stream().map(item -> {
            OrderItemVo orderItemVo = new OrderItemVo();
            orderItemVo.setSkuId(item.getSkuId());
            orderItemVo.setCount(item.getSkuQuantity());
            orderItemVo.setTitle(item.getSkuName());
            return orderItemVo;
        }).collect(Collectors.toList());

        lockVo.setLocks(locks);

        R r = wareFeignService.orderLockStock(lockVo);
        if(r.getCode() == 0){
//            所成功
        }else{
//            锁定失败

        }

        response.setCode(0);
        return response;
    }

    /**
     * 保存订单数据
     * @param orderTo
     */
    private void saveOrder(OrderCreateTo orderTo){
        OrderEntity order = orderTo.getOrder();
        order.setModifyTime(new Date());
        orderService.save(order);

//        保存订单项数据
        List<OrderItemEntity> orderItem = orderTo.getOrderItem();
        orderItemService.saveBatch(orderItem);

    }

    private OrderCreateTo createOrder(OrderSubmitVo vo,Long memberId){
        OrderCreateTo orderCreateTo = new OrderCreateTo();
//        生成订单号
        String orderSn = IdWorker.getTimeId();

        OrderEntity orderEntity = buildOrderEntity(orderSn,vo.getAddrId());
        orderEntity.setMemberId(memberId);

//      获取所有的订单项
        List<OrderItemEntity> orderItemEntities = buildOrderItemList(orderSn);

//        验价
        computePrice(orderEntity,orderItemEntities);

//        设置订单状态
        orderEntity.setStatus(0);

        orderEntity.setAutoConfirmDay(7);

        orderCreateTo.setOrder(orderEntity);
        orderCreateTo.setOrderItem(orderItemEntities);
        orderCreateTo.setFare(orderEntity.getFreightAmount());
        orderCreateTo.setPayPrice(orderEntity.getPayAmount());

        return orderCreateTo;
    }

    /**
     * 计算订单价格，积分，成长值
     * @param orderEntity
     * @param itemEntities
     */
    private void computePrice(OrderEntity orderEntity, List<OrderItemEntity> itemEntities){
//        订单价格相关
        BigDecimal total = new BigDecimal(0);
//        总优惠金额
        BigDecimal promotionTotal = new BigDecimal(0);

        BigDecimal couponTotal = new BigDecimal(0);

        BigDecimal integrationTotal = new BigDecimal(0);
        Integer totalInter = 0;
        Integer totalGrowth = 0;

        for (OrderItemEntity itemEntity : itemEntities) {
            promotionTotal.add(itemEntity.getPromotionAmount());
            couponTotal.add(itemEntity.getCouponAmount());
            integrationTotal.add(itemEntity.getIntegrationAmount());

            totalInter += itemEntity.getGiftIntegration();
            totalGrowth += itemEntity.getGiftGrowth();
            total = total.add(itemEntity.getRealAmount());
        }

        orderEntity.setTotalAmount(total);

        orderEntity.setPayAmount(total.add(orderEntity.getFreightAmount()));

        orderEntity.setPromotionAmount(promotionTotal);
        orderEntity.setCouponAmount(couponTotal);
        orderEntity.setIntegrationAmount(integrationTotal);
        orderEntity.setGrowth(totalGrowth);
        orderEntity.setIntegration(totalInter);

    }

    private OrderEntity buildOrderEntity(String orderSn,Long addrId){
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setOrderSn(orderSn);

//        获取收货地址信息
        R fare = wareFeignService.getFare(addrId);
        FareVo fareVo = fare.getData(new TypeReference<FareVo>(){});
//        orderCreateTo.setFare(fareVo.getFare());

        orderEntity.setFreightAmount(fareVo.getFare());
//        收货人信息
        orderEntity.setReceiverCity(fareVo.getAddress().getCity());
        orderEntity.setReceiverDetailAddress(fareVo.getAddress().getDetailAddress());
        orderEntity.setReceiverName(fareVo.getAddress().getName());
        orderEntity.setReceiverPhone(fareVo.getAddress().getPhone());
        orderEntity.setReceiverPostCode(fareVo.getAddress().getPostCode());
        orderEntity.setReceiverProvince(fareVo.getAddress().getProvince());
        orderEntity.setReceiverRegion(fareVo.getAddress().getRegion());
        return orderEntity;
    }

    /**
     * 构建所有的订单数据
     * @return
     */
    private List<OrderItemEntity> buildOrderItemList(String orderSn) {
        List<OrderItemVo> currentUserCartItems = cartFeignService.getCurrentUserCartItems();
        if(currentUserCartItems!=null&&currentUserCartItems.size() > 0){
            List<OrderItemEntity> orderItemEntityList = currentUserCartItems.stream().map(cartItem -> {
                OrderItemEntity itemEntity = buildOrderItem(cartItem);
                itemEntity.setOrderSn(orderSn);
                return itemEntity;
            }).collect(Collectors.toList());
            return orderItemEntityList;
        }

        return null;
    }

    /**
     * 构建单个订单项
     * @param cartItem
     * @return
     */
    private OrderItemEntity buildOrderItem(OrderItemVo cartItem) {
        OrderItemEntity itemEntity = new OrderItemEntity();
//        订单信息

//        spu信息  skuId
        R r = productFeignService.getSpuInfoBySkuId(cartItem.getSkuId());
        SpuInfoVo spuInfo = r.getData(new TypeReference<SpuInfoVo>() {
        });

        itemEntity.setSpuId(spuInfo.getId());
        itemEntity.setSpuBrand(spuInfo.getBrandId().toString());
        itemEntity.setSpuName(spuInfo.getSpuName());
        itemEntity.setCategoryId(spuInfo.getCatalogId());

//        sku信息
        itemEntity.setSkuId(cartItem.getSkuId());
        itemEntity.setSkuName(cartItem.getTitle());
        itemEntity.setSkuPic(cartItem.getImage());
        itemEntity.setSkuPic(cartItem.getPrice().toString());
        itemEntity.setSkuAttrsVals(StringUtils.collectionToDelimitedString(cartItem.getSkuAttr(),";"));
        itemEntity.setSkuQuantity(cartItem.getCount());
//        优惠信息

//        会员积分
        itemEntity.setGiftGrowth(cartItem.getPrice().multiply(new BigDecimal(cartItem.getCount())).intValue());
        itemEntity.setGiftIntegration(cartItem.getPrice().multiply(new BigDecimal(cartItem.getCount())).intValue());

        itemEntity.setPromotionAmount(new BigDecimal(0));
        itemEntity.setCouponAmount(new BigDecimal(0));
        itemEntity.setIntegrationAmount(new BigDecimal(0));

//        订单项的实际金额  单价 * 数量 - 各种优惠金额
        itemEntity.setRealAmount(itemEntity.getSkuPrice().multiply(new BigDecimal(itemEntity.getSkuQuantity())));

        return itemEntity;
    }

    /**
     * 参数可以是Message类型的原生消息
     * 也可以直接加消息题的类型
     *
     * RabbitListener使用在类上
     *
     * 可以有多个被标注rabbitHandler的方法处理
     * 这些方法处理的消息类型不同
     *
     * Channel
     * @param message
     */
//    @RabbitListener(queues = {"hello-queue"})
    @RabbitHandler
    public void rabbitListener(Message message,
                               OrderReturnReasonEntity entity,
                               Channel channel) throws IOException {
        byte[] body = message.getBody();
//        System.out.println("收到消息 ===>"+message+"  \n类型===>"+message.getClass());

        System.out.println("收到消息 ===> " + entity);

        long deliveryTag = message.getMessageProperties().getDeliveryTag();
//        手动确认消息  deliveryTag  在channel中是自增的
        try {
//            .....处理业务
            channel.basicAck(deliveryTag,false);
        } catch (IOException e) {
            e.printStackTrace();
//                                                 拒收消息后是否重新入队
//            long deliveryTag, boolean multiple, boolean requeue
            channel.basicNack(deliveryTag,false,true);
//            区别，不能批量拒绝
//            channel.basicReject(deliveryTag,true);
        }

//        System.out.println("数据通道 ==> " + channel);
    }

    @RabbitHandler
    public void rabbitListener(Message message,
                               OrderEntity entity,
                               Channel channel){
        byte[] body = message.getBody();
//        System.out.println("收到消息 ===>"+message+"  \n类型===>"+message.getClass());

        System.out.println("收到消息 ===> " + entity);

//        System.out.println("数据通道 ==> " + channel);
    }

}